#include "DrivenStrengthCellMovement.h"
#include<iostream>

#include "TimingEngine.hh"
#include "TimingIDBAdapter.hh"
#include "contest_db.h"

namespace ieda_contest {
#define Cw_1 0.16e-15
#define Rw_1 2.535
// #define DEBUG

DrivenStrengthCellMovement::DrivenStrengthCellMovement(ContestDB* database)
{
  _Contest_database = database;
}

/**
 * @brief Perform Load Optimization
 * 
 * @param quadratic_tdp 
 */
void DrivenStrengthCellMovement::runDrivenStrengthAwareOpt(QuadraticTDP* quadratic_tdp)
{
  LOG_INFO << "--------------------------------------------------------------------------------------------";
  LOG_INFO << "DrivenStrengthAwareOpt is started ! ! !";
  _quadratic_tdp = quadratic_tdp;
  _Contest_database->get_timing_engine()->updateTiming();
  for(int i = 0; i < 1; i++)
  {
    runBufferBalancing();
    _Contest_database->get_timing_engine()->updateTiming();
    runLoadOpt();
    _Contest_database->get_timing_engine()->updateTiming();
  }
  LOG_INFO << "DrivenStrengthAwareOpt is finished ! ! !";
  LOG_INFO << "--------------------------------------------------------------------------------------------";
}



// single method
void DrivenStrengthCellMovement::runClusteredMove()
{
  LOG_INFO << "--------------------------------------------------------------------------------------------";
  LOG_INFO << "ClusteredMove is started ! ! !";
  for (int i = 0; i < 1; i++) {
    runOneStepClusteredMovement(500);
    checkMaxDisplacementViolation(false);
  }  // end if
  LOG_INFO << "ClusteredMove is finished ! ! !";
  LOG_INFO << "--------------------------------------------------------------------------------------------";
}  // end for

void DrivenStrengthCellMovement::runBufferBalancing()
{
  LOG_INFO << "--------------------------------------------------------------------------------------------";
  LOG_INFO << "BufferBalancing is started ! ! !";

  int iter = 1;
  while (iter--) {
    std::deque<std::tuple<float, ContestInstance*>> buffers;

    for (auto cell : _Contest_database->get_ContestInstance_pvec()) {
      if (!cell->isBuffer() || cell->get_inpin_vec().size() != 1 || cell->get_outpin_vec().size() != 1)
        continue;

      ContestPin* ipin = nullptr;
      for (auto pin : cell->get_inpin_vec())
        ipin = pin;

      ContestPin* opin = nullptr;
      for (auto pin : cell->get_outpin_vec())
        opin = pin;

      auto inet = ipin->get_ContestNet();
      auto onet = opin->get_ContestNet();

      if (!inet || inet->get_pins().size() != 2)
        continue;

      if (!onet || onet->get_pins().size() != 2)
        continue;

      const double criticality = _quadratic_tdp->getCellCriticality(cell, Mode::LATE);
      // 这里要做修改！！！
      if (criticality >= 0.7) {
        buffers.push_back(std::make_tuple(criticality, cell));
      }  // end if
    }    // end for


    // Sort buffer by criticality.
    std::sort(buffers.begin(), buffers.end());

    // Equalize buffers.
    int movedBuffers = 0;
    int failed = 0;

    const int  numBuffers = buffers.size();
    for (int i = numBuffers - 1; i >= 0; i--) {
      auto buffer = std::get<1>(buffers[i]);
      if (!doBufferBalancing(buffer)) {
        failed++;
      }  // end if
      movedBuffers++;
    }  // end for
    LOG_INFO << "*******************************";
    LOG_INFO << "Need fix buffer nums :" << numBuffers;
    LOG_INFO << "buffer balancing failed nums :" << failed;
    LOG_INFO << "buffer balancing succeed nums :" << movedBuffers;
    LOG_INFO << "*******************************";
  }  // end while

  LOG_INFO << "BufferBalancing is finished ! ! !";
  LOG_INFO << "--------------------------------------------------------------------------------------------";
}

void DrivenStrengthCellMovement::runLoadOpt()
{
  LOG_INFO << "--------------------------------------------------------------------------------------------";
  LOG_INFO << "LoadOpt is started ! ! !";
  if (_optType == "steiner") {
    runMoveNonCriticalSinksOfCriticalNetsToSteinerPoint(_optionDontMoveRegistersAndLCBs);
  } else if (_optType == "driver") {
    runMoveNonCriticalSinksOfCriticalNetsToDriver(_optionDontMoveRegistersAndLCBs);
  } else {
    LOG_WARNING << " Invalid optimization type '" << _optType
                << "' "
                   "in load optimization step.\n";
  }  // end else
  LOG_INFO << "LoadOpt is finished ! ! !";
  LOG_INFO << "--------------------------------------------------------------------------------------------";
}

// utility
//  cluster utility
void DrivenStrengthCellMovement::runOneStepClusteredMovement(const int N)
{
  std::vector<std::pair<double, ContestPin*>> criticalPins;
  for (auto instance : _Contest_database->get_ContestInstance_pvec()) {
    Point<int32_t> initial_pos = instance->get_center_coord();
    instance->set_initial_pos(initial_pos);
    moved[instance] = false;
     for (auto pin : instance->get_pin_vec()) {
      double lSlack = pin->get_late_slack();
      if (lSlack < 0)
        criticalPins.emplace_back(lSlack, pin);
    }  // end for
  }

  std::sort(criticalPins.begin(), criticalPins.end());

  const int num = std::min(N, (int) criticalPins.size());
  for (int i = 0; i < num; i++) {
    // 有问题！！！
    if (moved[criticalPins[i].second->get_ContestInstance()])
      continue;

    clusterNeighborCriticalNets(criticalPins[i].second, true);
  }  // end for
}

void DrivenStrengthCellMovement::clusterNeighborCriticalNets(ContestPin* criticalPin, const bool dontMoveRegisters)
{
  const bool enableWeightening = false;

  auto criticalCell = criticalPin->get_ContestInstance();
  const double maxDist = 10 * static_cast<double>(_Contest_database->get_ContestLayout()->get_row_height());

  std::map<ContestNet*, bool> visited;

  for (auto net : _Contest_database->get_ContestNet_pvec())
    visited[net] = false;

  std::set<ContestInstance*> cells;
  std::list<std::pair<ContestNet*, int>> netList;

  auto cNet = criticalPin->get_ContestNet();

  if (!cNet)
    return;

  netList.emplace_back(cNet, 0);

  // do cluster
  do {
    ContestNet* curNet = netList.front().first;
    const int currDepth = netList.front().second;
    netList.pop_front();

    if (currDepth == 10)
      break;

    for (auto pin : curNet->get_pins()) {
      auto cell = pin->get_ContestInstance();

      if (!cell)
        continue;

      int32_t dist_x = cell->get_center_coord().get_x() - criticalCell->get_center_coord().get_x();
      int32_t dist_y = cell->get_center_coord().get_y() - criticalCell->get_center_coord().get_y();
      dist_x = std::abs(dist_x);
      dist_y = std::abs(dist_y);

      if (dist_x + dist_y > maxDist)
        continue;

      if (!cell->isFixed())
        cells.emplace(cell);

      for (auto cPin : cell->get_pin_vec()) {
        if (cPin == pin)
          continue;

        if (!(cPin->get_late_slack() < 0))
          continue;

        auto net = cPin->get_ContestNet();

        if (net == nullptr || visited[net])
          continue;

        visited[net] = true;
        netList.emplace_back(net, currDepth + 1);
      }
    }

  } while (!netList.empty());

  // TODO

  Point<int32_t> targetPosition(0, 0);
  int32_t targetPosition_x;
  int32_t targetPosition_y;
  double totalSlack = 0;
  /* [TODO] Put in another method */
  for (auto it = cells.begin(); it != cells.end(); it++) {
    ContestInstance* cell = *it;
    for (auto pin : cell->get_pin_vec()) {
      const auto net = pin->get_ContestNet();

      if (!net)
        continue;

      for (auto neightbor : net->get_pins()) {
        const double slack = neightbor->get_late_slack();
        if (!(slack < 0))
          continue;

        auto neightborCell = neightbor->get_ContestInstance();
        if (cells.find(neightborCell) != cells.end())
          continue;

        const double weight = enableWeightening ? (1 + _quadratic_tdp->getPinImportance(neightbor)) / 2 : 1;

        totalSlack += weight * slack;
        targetPosition_x += (int32_t) (weight * slack * neightbor->get_center_coord().get_x());
        targetPosition_y += (int32_t) (weight * slack * neightbor->get_center_coord().get_y());
        targetPosition.set_x(targetPosition_x);
        targetPosition.set_y(targetPosition_y);
      }
    }
  }

  int32_t norm_targetPosition_x = targetPosition.get_x() / totalSlack;
  targetPosition.set_x(norm_targetPosition_x);
  int32_t norm_targetPosition_y = targetPosition.get_y() / totalSlack;
  targetPosition.set_y(norm_targetPosition_y);

  Point<int32_t> clusterCenter(0, 0);
  double totalWeight = 0;
  int32_t clusterCenter_x;
  int32_t clusterCenter_y;

  for (auto it = cells.begin(); it != cells.end(); it++) {
    ContestInstance* cell = *it;
    if (enableWeightening) {
      double worstWeight = 0;
      for (auto pin : cell->get_pin_vec()) {
        const double weight = (1 + _quadratic_tdp->getPinImportance(pin) / 2);
        worstWeight = std::max(worstWeight, weight);
      }  // end for
      clusterCenter_x += worstWeight * cell->get_center_coord().get_x();
      clusterCenter_y += worstWeight * cell->get_center_coord().get_y();

      totalWeight += worstWeight;
    } else {
      clusterCenter_x += cell->get_center_coord().get_x();
      clusterCenter_y += cell->get_center_coord().get_y();

      totalWeight += 1;
    }
    clusterCenter.set_x(clusterCenter_x);
    clusterCenter.set_y(clusterCenter_y);
  }

  int32_t norm_clusterCenter_x = targetPosition.get_x() / totalWeight;
  clusterCenter.set_x(norm_clusterCenter_x);
  int32_t norm_clusterCenter_y = targetPosition.get_y() / totalWeight;
  clusterCenter.set_y(norm_clusterCenter_y);

  int32_t diff_x = targetPosition.get_x() - clusterCenter.get_x();
  int32_t diff_y = targetPosition.get_y() - clusterCenter.get_y();
  Point<int32_t> diff(diff_x, diff_y);

  for (std::set<ContestInstance*>::iterator it = cells.begin(); it != cells.end(); it++) {
    if (dontMoveRegisters && (*it)->isFlipflop())
      continue;

    int32_t newPos_x = (*it)->get_center_coord().get_x() + diff.get_x();
    int32_t newPos_y = (*it)->get_center_coord().get_y() + diff.get_y();
    Point<int32_t> newPos(newPos_x, newPos_y);

    double oldCost, newCost;

    ContestInstance* instance = *it;
    CostType costFunction = COST_LOCAL_DELAY;
    _quadratic_tdp->moveCellWithCostEvaluation(instance, newPos.get_x(), newPos.get_y(), oldCost, newCost, costFunction);
    moved[*it] = true;
  }
}

// buffer balancing utility
bool DrivenStrengthCellMovement::doBufferBalancing(ContestInstance* buffer)
{
  const Point<int32_t> oldPos = buffer->get_center_coord();
  if (buffer->isFixed()) {
    return false;  // don't move fixed cells
  }                // end if

  ContestPin* ipin = nullptr;  
  for (auto pin : buffer->get_pin_vec())
    ipin = pin;  // 类指针只是一个指针变量，而不是一个新的对象，所以没有创建新对象，所以这里只是指针变量的赋值操作，并未创建新的对象，所以不涉及构造函数。

  ContestPin* opin = nullptr;
  for (auto pin : buffer->get_outpin_vec())
    opin = pin;  // should be the only output pin

  auto inet = ipin->get_ContestNet();
  auto onet = opin->get_ContestNet();

  auto driver = inet->get_driver_pin();
  if (driver->isPI() || driver->isPO() || driver->isPIO() || driver->isClockPin()) {
    return false;  // [TODO] Skip for now, in the future use input driver if available...
  }                // end if

  ContestPin* sink;
  for (auto pin : onet->get_sink_pins())
    sink = pin;  // should be the only sink pin

  const double R0 = _quadratic_tdp->getPinResistance(ipin);
  const double R1 = _quadratic_tdp->getPinResistance(driver);
  const double C1 = _quadratic_tdp->getPinCap(ipin);
  const double C2 = _quadratic_tdp->getPinCap(sink);

  const Point<int32_t> driverPos = driver->get_center_coord();
  const Point<int32_t> ipinPos = ipin->get_center_coord();
  const Point<int32_t> opinPos = opin->get_center_coord();
  const Point<int32_t> sinkPos = sink->get_center_coord();

  const double d = _quadratic_tdp->computeManhattanDistance(driverPos, sinkPos);
  const double a = 0;

  if (std::abs(d) <= 1e-6) {
    return false;
  }  // end if

  const double d1
      = std::min(d - a, std::max(0.0, (Cw_1 * R1 - Cw_1 * R0 + Rw_1 * C2 - Rw_1 * C1 + (Cw_1 * d - a * Cw_1) * Rw_1) / (2 * Cw_1 * Rw_1)));

  const double dx = sinkPos.get_x() - driverPos.get_x();
  const double dy = sinkPos.get_y() - driverPos.get_y();
  const double scaling = d1 / d;

  const double px = scaling * dx + driverPos.get_x();
  const double py = scaling * dy + driverPos.get_y();

  bool success = false;
  if (!_quadratic_tdp->moveCell(buffer, px, py)) {
      std::cout << "[WARNING]: move error at " << __FILE__ << ": " << __LINE__ << "\n";

  } else {
    success = true;  // moveCell(cell, avgPx, avgPy);
  }

  // 旧方法！！！
  //  double oldCost;
  //  double newCost;
  //  CostType costFunction = COST_LOCAL_DELAY;
  //  const bool success = _quadratic_tdp->moveCellWithCostEvaluation(buffer, (int32_t) px, (int32_t) py, oldCost, newCost, costFunction);
  return success;
}  // end method


// load opt utility
void DrivenStrengthCellMovement::runMoveNonCriticalSinksOfCriticalNetsToSteinerPoint(const bool DontMoveRegistersAndLCBs)
{
  std::map<ContestInstance*, bool> visited;
  for (auto cell : _Contest_database->get_ContestInstance_pvec()) {
    visited[cell] = false;
  }  // end for

  const double lateSlackThreshold = 0;
  const double earlySlackThreshold = 0;

  int counterMoved = 0;
  int counterLegalizationFail = 0;

  std::vector<std::tuple<double, ContestNet*>> orderedNets;
  orderedNets.reserve(_Contest_database->get_ContestNet_pvec().size());
  for (auto net : _Contest_database->get_ContestNet_pvec()) {
    std::optional<double> wire_cap = std::nullopt;
    orderedNets.push_back(std::make_tuple(_quadratic_tdp->getNetCriticality(net) * wire_cap.value(), net));
  }  // end for
  std::sort(orderedNets.begin(), orderedNets.end());
  const int numElements = orderedNets.size();
  for (int i = numElements - 1; i >= 0; i--) {
    ContestNet* net = std::get<1>(orderedNets[i]);  // std::get<>用来获得tuple对象里的元素的。

    // Get the driver of the net.
    auto driver = net->get_driver_pin();

    if (!driver || driver->get_late_slack() >= 0 || driver->get_early_slack() <= 0)
      continue;

    for (auto sink : net->get_sink_pins()) {
      auto cell = sink->get_ContestInstance();
      if (!cell)
        continue;

      // if (cell.isPort())
      // 	continue;
      if (cell->isBoundaryCell())
        continue;

      if (visited[cell])
        continue;
      else
        visited[cell] = true;

      if (cell->isFixed() || (DontMoveRegistersAndLCBs && (cell->isBuffer() || cell->isFlipflop())))
        continue;

      const Point<int32_t> lowerCellPos = cell->get_center_coord();

      bool isCritical = false;

      for (auto pin : cell->get_outpin_vec()) {
        if (pin->get_late_slack() < lateSlackThreshold || pin->get_early_slack() < earlySlackThreshold) {
          isCritical = true;
          break;
        }  // end if
      }    // end if

      // TODO:RCtree
      if (!isCritical) {
        // const Rsyn::RCTree &tree = routingEstimator->getRCTree(net);

        // // Find the node in three that connects to this cell.
        // const int numNodes = tree.getNumNodes();
        // for (int i = 0; i < numNodes; i++) {
        // 	const Rsyn::RCTreeNodeTag &tag0 = tree.getNodeTag(i);
        // 	if (tag0.getPin() && (tag0.getPin().getInstance() == cell)) {
        // 		const Rsyn::RCTree::Node &node = tree.getNode(i);

        // 		if (node.propParent != -1) { // defensive programming (sink should not be at the root)
        // 			const Rsyn::RCTreeNodeTag &tag1 = tree.getNodeTag(node.propParent);

        // 			const double dx = tag1.x - tag0.x;
        // 			const double dy = tag1.y - tag0.y;
        // 			if (!infra->translateCell(cell, (DBU) dx, (DBU) dy, LEG_NEAREST_WHITESPACE))
        // 					counterLegalizationFail++;
        // 			counterMoved++;
        // 		} // end else

        // 		break;
        // 	} // end if
        // } // end for
      }  // end if
    }    // end for
  }      // end for
}

void DrivenStrengthCellMovement::runMoveNonCriticalSinksOfCriticalNetsToDriver(const bool DontMoveRegistersAndLCBs)
{
  std::map<ContestInstance*, bool> visited;
  for (auto cell : _Contest_database->get_ContestInstance_pvec()) {
    visited[cell] = false;
  }  // end for

  const double lateSlackThreshold = 0;
  const double earlySlackThreshold = 0;

  int counterMoved = 0;
  int counterLegalizationFail = 0;

  std::vector<std::tuple<double, ContestNet*>> orderedNets;
  orderedNets.reserve(_Contest_database->get_ContestNet_pvec().size());
  for (auto net : _Contest_database->get_ContestNet_pvec()) {
    // TODO:wire_cap
    double wirelength = 0;
    auto source = net->get_driver_pin();
    if(source == nullptr)
      continue;
    double max_x = source->get_center_coord().get_x();
    double max_y = source->get_center_coord().get_y();
    double min_x = source->get_center_coord().get_x();
    double min_y = source->get_center_coord().get_y();

    for (auto pin : net->get_pins()) {
      if (pin == source)
        continue;
      max_x = std::max(static_cast<double>(pin->get_center_coord().get_x()), max_x);
      max_y = std::max(static_cast<double>(pin->get_center_coord().get_y()), max_y);
      min_x = std::min(static_cast<double>(pin->get_center_coord().get_x()), min_x);
      min_y = std::min(static_cast<double>(pin->get_center_coord().get_y()), min_y);
    }
    wirelength = std::abs(max_x - min_x) + std::abs(max_y - min_y);
    double wirelength_dbu = wirelength / _Contest_database->get_ContestLayout()->get_database_unit();
    auto* timing_engine = ista::TimingEngine::getOrCreateTimingEngine();
    auto* db_adapter = timing_engine->get_db_adapter();
    std::optional<double> width = std::nullopt;
    double wire_cap = dynamic_cast<ista::TimingIDBAdapter*>(db_adapter)
                          ->getCapacitance(3, wirelength_dbu, width);
#ifdef DEBUG
    std::cout << "net name: " << net->get_name() << " wirelength: " << wirelength << " wire_cap: " << wire_cap << std::endl;
#endif

    orderedNets.push_back(std::make_tuple(_quadratic_tdp->getNetCriticality(net) * wire_cap, net));
  }  // end for

  std::sort(orderedNets.begin(), orderedNets.end());

  const int numElements = orderedNets.size();
  for (int i = numElements - 1; i >= 0; i--) {
    // 处理所有nets。选择net的加权线电容最大的先处理！！！
    ContestNet* net = std::get<1>(orderedNets[i]);  // std::get<>用来获得tuple对象里的元素的。
    // Get the driver of the net.
    auto driver = net->get_driver_pin();

    if (!driver || driver->get_late_slack() >= 0)
      continue;

    for (auto sink : net->get_sink_pins()) {
      auto cell = sink->get_ContestInstance();

      if (!cell)
        continue;
      // if (cell->isBoundaryCell())
      //   continue;

      if (visited[cell])
        continue;
      else
        visited[cell] = true;

      if (cell->isFixed() || (DontMoveRegistersAndLCBs && (cell->isBuffer() || cell->isFlipflop())))
        continue;

      const Point<int32_t> lowerCellPos = cell->get_center_coord();

      bool isCritical = false;

      // net的sink对应的cell outpin 不重要的cell，只要cell的outpin slack < 0 就认为这个cell是critical的！
      for (auto pin : cell->get_outpin_vec()) {
        if (pin->get_late_slack() < lateSlackThreshold) {
          isCritical = true;
          break;
        }  // end if
      }    // end if
      // 方法一：需要评估cost
      //  if (!isCritical) {
      //    ContestInstance* phCellDriver = driver->get_ContestInstance();
      //    const Point<int32_t> lowerDriverPos = phCellDriver->get_center_coord();

      //   const int32_t dx = lowerDriverPos.get_x() - lowerCellPos.get_x();
      //   const int32_t dy = lowerDriverPos.get_y() - lowerCellPos.get_y();
      //   double oldCost;
      //   double newCost;

      //   if (!_quadratic_tdp->translateCellWithCostEvaluation(cell, dx, dy, oldCost, newCost)) {
      //     counterLegalizationFail++;
      //   }  // end if

      //   counterMoved++;
      // }  // end if

      // 方法二：直接移动，无需评估，减少时间！
      if (!isCritical) {
        ContestInstance* phCellDriver = driver->get_ContestInstance();
        const Point<int32_t> lowerDriverPos = phCellDriver->get_center_coord();
        int32_t original_x = lowerCellPos.get_x();
        int32_t original_y = lowerCellPos.get_y();

        const int32_t dx = lowerDriverPos.get_x() - lowerCellPos.get_x();
        const int32_t dy = lowerDriverPos.get_y() - lowerCellPos.get_y();
        if (!_quadratic_tdp->moveCell(cell,original_x+dx, original_y+dy)) {
          if (!_quadratic_tdp->moveCell(cell,original_x,original_y)) {
            std::cout << "[WARNING]: move error at " << __FILE__ << ": " << __LINE__ << "\n";
          }  // end if
            counterLegalizationFail;
        } else {
          counterMoved++;  // moveCell(cell, avgPx, avgPy);
        }                  // end if
      }  // end if
    }    // end for
  }      // end for

  LOG_INFO << "*******************************";
  LOG_INFO << "load opt failed nums :" << counterLegalizationFail;
  LOG_INFO << "load opt succeed nums :" << counterMoved;
  LOG_INFO << "*******************************";
}

bool DrivenStrengthCellMovement::checkMaxDisplacementViolation(const bool hideMessageIfPass)
{
  if (!_clsEnableMaxDisplacementConstraint)
    return true;

  const int maxViolatorsToReport = 10;

  vector<ContestInstance*> violators;
  violators.reserve(maxViolatorsToReport);

  bool moreViolators = false;
  for (auto cell : _Contest_database->get_ContestInstance_pvec()) {
    if (_quadratic_tdp->violateMaxDisplacement(cell)) {
      if (violators.size() >= maxViolatorsToReport) {
        moreViolators = true;
        break;
      } else {
        violators.push_back(cell);
      }  // end if
    }    // end if
  }      // end for

  const bool pass = violators.empty();
  if (!pass || !hideMessageIfPass) {
    std::cout << "[" << (violators.size() > 0 ? "FAIL" : "PASS") << "] ";

    for (unsigned i = 0; i < violators.size(); i++) {
      ContestInstance* cell = violators[i];
      Point<int32_t> posCell = cell->get_center_coord();
      Point<int32_t> initial = cell->get_initial_pos();
      const double ox = initial.get_x();
      const double oy = initial.get_y();
      const double x = posCell.get_x();
      const double y = posCell.get_y();

      const double displacement = _quadratic_tdp->computeManhattanDistance(posCell, initial);

      std::cout << "\t";
      std::cout << cell->get_name() << " "
                << "ox=" << ox << " "
                << "oy=" << oy << " "
                << "x=" << x << " "
                << "y=" << y << " "
                << "displacement=" << displacement << " "
                << "(" << _quadratic_tdp->getMaxDisplacement() << ")\n";
    }  // end for

    if (moreViolators) {
      std::cout << "\t";
      std::cout << "At least one more violator... Stopping here.\n";
    }  // end if
  }    // end if

  return violators.size() == 0;
}








// bool DrivenStrengthCellMovement::checkUnlegalizedCells()
// {
//   ///\brief 用合法化来检查单元的合法性！
// }
// void DrivenStrengthCellMovement::reportTiming()
// {
//   for (std::string clock_name : iPLAPIInst.obtainClockNameList()) {
//     double wns = iPLAPIInst.obtainWNS(clock_name.c_str(), ista::AnalysisMode::kMax);
//     double tns = iPLAPIInst.obtainTNS(clock_name.c_str(), ista::AnalysisMode::kMax);
//     if (_iter == 0) {
//       _original_tns = tns;
//       _original_wns = wns;
//       _iter++;
//     }
//     LOG_INFO << "************* 1.4 DrivenStrengthCellMovement **************" << std::endl;
//     LOG_INFO << "Original  Timing  Information ! ! ! ";
//     LOG_INFO << "Clock : " << clock_name << " WNS : " << _original_wns << ";"
//              << " TNS : " << _original_tns << std::endl;
//     LOG_INFO << "***************************************" << std::endl;
//     LOG_INFO << "Current Opt Timing Information ! ! ! ";
//     LOG_INFO << "Clock : " << clock_name << " WNS : " << wns << ";"
//              << " TNS : " << tns << std::endl;
//     LOG_INFO << "************* 1.4 DrivenStrengthCellMovement **************" << std::endl;
//   }
// }
// void DrivenStrengthCellMovement::finalImproveRadio()
// {
//   for (std::string clock_name : iPLAPIInst.obtainClockNameList()) {
//     double wns = iPLAPIInst.obtainWNS(clock_name.c_str(), ista::AnalysisMode::kMax);
//     double tns = iPLAPIInst.obtainTNS(clock_name.c_str(), ista::AnalysisMode::kMax);
//     LOG_INFO << "************* 1.4 DrivenStrengthCellMovement **************" << std::endl;
//     LOG_INFO << "Original  Timing  Information ! ! ! ";
//     LOG_INFO << "Clock : " << clock_name << " WNS : " << _original_wns << ";"
//              << " TNS : " << _original_tns << std::endl;
//     LOG_INFO << "***************************************" << std::endl;
//     LOG_INFO << "Final Opt Timing Information ! ! ! ";
//     LOG_INFO << "Clock : " << clock_name << " WNS : " << wns << ";"
//              << " TNS : " << tns << std::endl;
//     LOG_INFO << "***************************************" << std::endl;
//     LOG_INFO << "WNS Improve Radio :" << std::showpos << 100 * ((_original_wns - wns) / _original_wns) << "\n";
//     LOG_INFO << "TNS Improve Radio :" << std::showpos << 100 * ((_original_tns - tns) / _original_tns) << "\n";
//     LOG_INFO << "************* 1.4 DrivenStrengthCellMovement **************" << std::endl;
//   }
// }

};  // namespace ieda_contest